# chapter.py
"""
Objects and tools for working with MKV Chapters for Lib-Ray.
"""

import os, subprocess, string
import xml.etree.ElementTree as ET

from userinput import *

from config import TEMPDIR, DISTPATH, TESTDIR

           
class ChapterSet(object):
      """
      Aggregate of chapter objects for a title.
      
      XML self-consistency check:
      
      >>> import chapter
      >>> c = chapter.ChapterSet()
      >>> c.from_xml(chapter.test_xml)
      >>> x1 = c.xml()
      >>> d = chapter.ChapterSet()
      >>> d.from_xml(x1)
      >>> x2 = d.xml()
      >>> x1 == x2
      True
      >>>
      """
      xml_start = """\
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Chapters SYSTEM "matroskachapters.dtd">
  <Chapters>
  <EditionEntry>
"""
      
      xml_end   = """\
  </EditionEntry>
  </Chapters>
"""
      
      def __init__(self):
          self.chapters = []
          self.n_per_page = 4
          self.template = None
          
      def __str__(self):
          s  = "CHAPTER SET\n"
          s += " template=%s\n" % self.template
          s += " n_per_page=%d\n" % self.n_per_page
          for chapter in self.chapters:
              s += str(chapter)
          s += ".\n"
          return s
          
      def __repr__(self):
          d = {
              'n_per_page': self.n_per_page,
              'template':   self.template
              }             
          c = []
          for chapter in self.chapters:
              c.append(chapter._dict_repr())
          s = "CHAPTERSET: " + repr( (d,c) )
          return s
          
      def from_repr(self, r):
          if r[:11] != 'CHAPTERSET:':
              raise ValueError("Not a chapter representation.")
          d = eval(r[11:])
          self.n_per_page = d[0]['n_per_page']
          self.template   = d[0]['template']
          self.chapters = []
          for c in d[1]:
              ch = Chapter()
              ch._from_dict(c)
              self.chapters.append(ch)
         
      def add_chapter(self, chapter):
          self.chapters.append(chapter)
          
      def xml(self, language='eng'):
          """
          Return an XML representation of the chapter set, suitable for use with mkvmerge.
          """
          xml = self.xml_start
          for chapter in self.chapters:
              xml += chapter.xml(language=language)
          xml += self.xml_end
          return xml
          
      def from_xml(self, xml):
          """
          Load chapter set from Matroska Chapters XML.
          """
          # FIXME: Currently this only works with the chapters files created
          #        by the Lib-Ray wizard. We should make this more flexible
          #        so it can read more complex chapter files, but we don't
          #        yet support all of the options.
          ee = ET.fromstring(xml)[0]
          for atom in ee:
              name = atom.find('ChapterDisplay').find('ChapterString').text
              time = atom.find('ChapterTimeStart').text
              c = Chapter()
              c.new(name, timecode(time))
              self.chapters.append(c)
          
      def interactive(self):
          """
          Interactively get chapter set information from user.
          """
          self.template_path = userinput(
              "Basic template to use for chapter pages?",
              default=os.path.join(DISTPATH, 'templates/default/chapterpage.html'),
              inp=file_inp)
          self.n_per_page = userinput(
              "How many chapters should appear on each chapter page?",
              default=4, inp=int)
          self.chapter_cols = userinput(
              "How many chapters should appear in each row?",
              default=int(self.n_per_page**0.5)+1,
              inp=int)
          self.chapters = []
          while True:
              more = userinput(
                "Add a chapter?", 
                default=True, inp=yn_inp, out=yn_out)
              if not more:
                  break
              self.chapters.append(Chapter())
              self.chapters[-1].interactive()
              
              
class Chapter(object):
      """
      Information about a chapter to be included in the Lib-Ray Video file.
      """
      xml_string="""\
    <ChapterAtom>
      <ChapterTimeStart>{TIMECODE!s}</ChapterTimeStart>
      <ChapterDisplay>
        <ChapterString>{NAME!s}</ChapterString>
        <ChapterLanguage>{LANGUAGE!s}</ChapterLanguage>
      </ChapterDisplay>
    </ChapterAtom>
"""
      
      def __init__(self):
          self.name = ""
          self.time = None
          self.thumbnail = None
          self.longname = self.name
          
      def __str__(self):
          s =  "\t%s - %s\n" % (self.time, self.name)
          s += "\tMenu Name: %s\n" % self.longname
          s += "\tMenu Thumbnail: %s\n" % self.thumbnail
          return s
          
      def _dict_repr(self):
          d = {
              'name': self.name, 
              'time': self.time, 
              'longname': self.longname,
              'thumbnail': self.thumbnail
               }
          return d
          
      def __repr__(self):
          return 'CHAPTER: %s' % repr(d)
      
      def from_repr(self, r):
          if r[:8] != 'CHAPTER:':
              raise ValueError("Not a chapter representation.")
          d = eval(r[8:])
          self._from_dict(d)
          
      def _from_dict(self, d):
          self.name = d['name']
          self.time = d['time']
          self.longname = d['longname']
          self.thumbnail = d['thumbnail']
          
      def xml(self, language='eng'):
          """
          Return XML chapter code for this chapter.
          """
          return self.xml_string.format(TIMECODE=timecode(self.time), NAME=self.name, LANGUAGE=language)

      def new(self, name, time):
          """
          Load chapter data.
          """
          self.name = name
          self.longname = name
          self.time = time
          
          
      def interactive(self):
          """
          Interactively query the user for chapters information.
           
          II> c = Chapter()
          II> c.interactive()
          Chapter title? [] The mole had been busy all morning...
          Chapter start time? [None] 0:00
          II> 
          II> str(c)
          '00:00:0.000 - The_mole_had_been_busy_all_morni'
          II>
          """
          self.name = userinput(
              "Chapter title?", 
              default=self.name,
              inp=ident_inp)
          self.time = userinput(
              "Chapter start time?", 
              default=self.time,
              inp=timecode)
          self.thumbnail = userinput(
              "Chapter thumbnail image for menus?",
              default='chapterdef.png',
              inp=file_inp)
          self.longname = userinput(
              "Chapter title for menus?",
              default=self.longname)


# For unit-testing              
test_xml = """\
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Chapters SYSTEM "matroskachapters.dtd">
  <Chapters>
  <EditionEntry>
    <ChapterAtom>
      <ChapterTimeStart>00:00:0.000</ChapterTimeStart>
      <ChapterDisplay>
        <ChapterString>First_Chapter</ChapterString>
        <ChapterLanguage>eng</ChapterLanguage>
      </ChapterDisplay>
    </ChapterAtom>
    <ChapterAtom>
      <ChapterTimeStart>00:13:1.010</ChapterTimeStart>
      <ChapterDisplay>
        <ChapterString>Second_Chapter</ChapterString>
        <ChapterLanguage>eng</ChapterLanguage>
      </ChapterDisplay>
    </ChapterAtom>
  </EditionEntry>
  </Chapters>
"""

            
if __name__ == "__main__":
    import doctest
    doctest.testmod()



